(function () {
    "use strict";

    require("./slice-args");
    require("./assign");
    var Notifier = require("./notifier");
    var defineType = require("./define-type");
    var PathWalker = require("./path-walker");

    function itemChange(list, e) {
        var index = list.items.indexOf(e.target);
        var e = Object.assign({}, e, {
            target: list,
            index: index,
            property: PathWalker.join("items", index, e.property)
        });
        if (list.frozen) {
            Notifier.addToFrozenEvents(list, e);
        } else {
            console.error("triggering from item change");
            list.trigger("change", e);
        }
    }

    function subscribeItems(list, items) {
        return items.map(function (item) {
            var handler = null;
            if (!Notifier.isNotifier(item)) {
                return handler;
            }

            var existingIndex = list.items.indexOf(item);
            if (existingIndex < 0) {
                var handler = function (e) {
                    itemChange(list, e);
                };
                item.on("change", handler);
            } else {
                handler = list.itemHandlers[existingIndex];
            }

            return handler;
        });
    }

    function unsubscribeItems(list, items, handlers) {
        items.forEach(function (item, index) {
            var handler = handlers[index];
            if (Notifier.isNotifier(item) &&
                list.items.indexOf(item) < 0) {
                item.off("change", handler);
            }
        });
    }

    var NotifierList = defineType(function (items) {
        this.items = [];
        this.itemHandlers = [];
        if (items) {
            items.unshift(0, 0);
            this.splice.apply(this, items);
        }
    }, new Notifier, {
        splice: function (index, length) {
            if (arguments.length < 2) {
                return 0;
            }

            if (length < 0) {
                length = this.items.length;
            }

            var args = Function.sliceArgs(arguments);
            var newItems = args.slice(2);
            var newHandlers = subscribeItems(this, newItems);

            args[1] = length;
            var oldItems = this.items.splice.apply(this.items, args);

            args = newHandlers.slice();
            args.unshift(index, length);
            var oldHandlers = this.itemHandlers.splice.apply(this.itemHandlers, args);

            unsubscribeItems(this, oldItems, oldHandlers);

            var e = {
                listChange: true,
                property: "items",
                target: this,
                index: index,
                oldItems: oldItems,
                newItems: newItems
            };
            if (this.frozen) {
                Notifier.addToFrozenEvents(this, e);
            } else {
                console.error("triggering from list change");
                this.trigger("change", e);
            }

            return oldItems;
        },

        append: function () {
            var args = Function.sliceArgs(arguments);
            args.unshift(this.items.length, 0);
            this.splice.apply(this, args);
        },

        remove: function (item) {
            var index = this.items.indexOf(item);
            if (index < 0) {
                return;
            }

            this.splice(index, 1);
        },

        setItems: function (items) {
            items.unshift(0, -1);
            this.splice.apply(this, items);
        }
    });

    module.exports = NotifierList;
})();
